#include <stdio.h>
#include <stdlib.h>
#include <string.h>



struct slots{
	int id;
	int value;
	struct slots* next;
};

struct stringobj{
	int id;
	struct slots* empty;
	char* string;
};



void printfloat(float val){
	printf("Base Float: %f \n", val);
}

void printfloatfromptr(float* val){
//	printf("Float Ptr: %d\n", ((int)val));
//	printf("Float Val from Ptr: %f\n", *val);
}

void printstrfromptr(char* val){
//	printf("Char Ptr: %c\n", ((int)val));
//	printf("Char Val: %s\n", *val);
}

unsigned int stringLength(int maskedStrPtr) {
	int* unmaskedPtr = (int*)(maskedStrPtr & 0xFFFFFFFC);
	if(unmaskedPtr[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("String Length Must be called on a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr = (char*)(unmaskedPtr[2]);
	int count = 0;
	while(*strPtr != 0){
		++strPtr;
		++count;
	}
	return count << 2;
}

unsigned int subString(int maskedStrPtr, int start, int end) {
	//printf("Substring! start:%d, end:%d\n", start, end);
	start = start >> 2;
	end = end >> 2;
	//printf("Substring! later  start:%d, end:%d\n", start, end);
	int length = end-start+1;
	
	int* unmaskedPtr = (int*)(maskedStrPtr & 0xFFFFFFFC);
	
	
	if(unmaskedPtr[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("Sub String Must be called on a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr = (char*)(unmaskedPtr[2]);	
	char* newString = (char*)malloc(length);
	
	
	int i = start;
	int count = 0;	
	for(i; i < end; i++) {
		newString[count] = strPtr[i];
		++count;
	}
	newString[count] = '\0';
	//printf("String: %s\n", newString);
	return (unsigned int) newString;
}

unsigned int stringEq(int maskedStrPtr1, int maskedStrPtr2) {
	int* unmaskedPtr1 = (int*)(maskedStrPtr1 & 0xFFFFFFFC) ;
	if(unmaskedPtr1[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("String Equal's first argument must be a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr1 = (char*)(unmaskedPtr1[2]);
	int* unmaskedPtr2 = (int*)(maskedStrPtr2 & 0xFFFFFFFC) ;
	if(unmaskedPtr2[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("String Equal's second argument must be a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr2 = (char*)(unmaskedPtr2[2]);
	
	return (strcmp(strPtr1, strPtr2)== 0) ? 7:3;
}

unsigned int stringLessThan(int maskedStrPtr1, int maskedStrPtr2) {
	int* unmaskedPtr1 = (int*)(maskedStrPtr1 & 0xFFFFFFFC) ;
	if(unmaskedPtr1[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("String Less Than's first argument must be a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr1 = (char*)(unmaskedPtr1[2]);
	int* unmaskedPtr2 = (int*)(maskedStrPtr2 & 0xFFFFFFFC) ;
	if(unmaskedPtr2[0] != 2) { // wrong type
		printf("---------------------------------------\n");
		printf("string Less Than's second argument must be a String object!\n");
		printf("---------------------------------------\n");
		exit(1);
	}
	char* strPtr2 = (char*)(unmaskedPtr2[2]);
	printf("one: %s, two: %s, compared: %d\n", strPtr1, strPtr2,strcmp(strPtr1, strPtr2));
	
	return (strcmp(strPtr1, strPtr2) < 0) ? 7:3;
}

unsigned int getVarFromScope(int eframe,int numberOfLevels, int numberOver) {
	int* eFramePointer = (int*)(eframe - 1);
//	printf("Scope Pointer: %d\n", eFramePointer);
	int i;
	for(i = 0; i < numberOfLevels; i++) {
		eFramePointer = (int*)(*eFramePointer);
//		printf("Pointer updated!: %d\n", eFramePointer);
	}
	int* intaddr = (eFramePointer + (numberOver+1));
//	printf("Final int ptr: %d\n", intaddr);
//	printf("Stored value: %d\n", *intaddr);
	return *intaddr;
}


unsigned int getVarFromSlots(int slots,int var, int emptySlots) {
	struct slots* slotsPointer = (struct slots*)(slots);
//	printf("getVarFromSlots\n");
//	printf("emptySlots: %d\n", emptySlots);
//	printf("var: %d\n", var);
	int i;
	while(slotsPointer != (struct slots*)emptySlots){
//		printf("Slots Pointer: %d\n", slotsPointer);
//		printf("Slots id: %d\n", (*slotsPointer).id);
//		printf("Slots value: %d\n", (*slotsPointer).value);
//		printf("Slots next: %d\n", (*slotsPointer).next);
		int thisTag = (*slotsPointer).id;	
		if(thisTag == var){
			return (*slotsPointer).value;	
		}
//		printf("Pointer updated!: %d\n", slotsPointer);

		slotsPointer = (*slotsPointer).next;
	}
	printf("---------------------------------------\n");
	printf("Could not find member in object!\n");
	printf("---------------------------------------\n");
	exit(1);
}

unsigned int mutateSlotsVar(int slots, int var, int value, int emptySlots) {
	struct slots* slotsPointer = (struct slots*)(slots);
//	printf("mutateSlotsVar\n");
//	printf("emptySlots: %d\n", emptySlots);
//	printf("var: %d\n", var);
	int i;
	struct slots* lastSlotsPointer = slotsPointer;
	while(slotsPointer != (struct slots*) emptySlots){
//		printf("Slots Pointer: %d\n", slotsPointer);
//		printf("Slots id: %d\n", (*slotsPointer).id);
//		printf("Slots value: %d\n", (*slotsPointer).value);
//		printf("Slots next: %d\n", (*slotsPointer).next);
		int thisTag = (*slotsPointer).id;	
		if(thisTag == var){
			(*slotsPointer).value = value;	
		}
//		printf("Pointer updated!: %d\n", slotsPointer);

		lastSlotsPointer = slotsPointer;
		slotsPointer = (*slotsPointer).next;
	}
	
	slotsPointer = lastSlotsPointer;
	
	struct slots* newSlots = (struct slots*)malloc(sizeof(struct slots));
	(*newSlots).id = var;
	(*newSlots).value = value;
	(*newSlots).next = (struct slots*) emptySlots;
	(*slotsPointer).next = newSlots;
//	printf("newSlots Pointer: %d\n", newSlots);
//	printf("newSlots id: %d\n", (*newSlots).id);
//	printf("newSlots value: %d\n", (*newSlots).value);
//	printf("newSlots next: %d\n", (*newSlots).next);
//	printf("\nSlots Pointer: %d\n", slotsPointer);
//	printf("Slots id: %d\n", (*slotsPointer).id);
//	printf("Slots value: %d\n", (*slotsPointer).value);
//	printf("Slots next: %d\n", (*slotsPointer).next);
	return;
}

void storeVarInScope(int eframe, int numberOfLevels, int numberOver, int value) {
	int* eFramePointer = (int*)(eframe - 1);
//	printf("Scope Pointer: %d\n", eFramePointer);
	int i;
	for(i = 0; i < numberOfLevels; i++) {
		eFramePointer = (int*)(*eFramePointer);
//		printf("Pointer updated!: %d\n", eFramePointer);
	}
	int* intaddr =  ((int*)(eFramePointer + (numberOver+1)));
//	printf("Final int ptr: %d\n", intaddr);
	*intaddr = value;
//	printf("Stored value: %d\n", *intaddr);
}


unsigned int read_line(){
	char sentence[ 300 ];
	char* sent = gets(sentence);
	//printf("Sentence = %s at %d\n ", sent, sent);
	int intSent = (int) sent;
	//printf("Pointer = %d\n", intSent);
	//struct stringobj* string = malloc(sizeof(struct stringobj));
	//string[0] = 1;
	//string[1] = 0;
	//string[2] = sent;
	//printf("Stringptr = %d | val = %d\n", string, string[0]);	
	
	return (unsigned int) sent ;
}


void printvalue(unsigned int val){
	int type = val & 0x3;
//	printf("val %d -> ", val);
	switch(type){
		case 0: // int
//			printf("type int = %d\n", type);
//			printf("Int: %d\n", val>>2);
			printf("%d\n", val>>2);
		break;
		case 1: // pointer
		{
			int* unmaskedPtr = (int*)(val & 0xFFFFFFFC) ;
//			printf("type pointer = %d\n", type);
//			printf("unmaskedPtr: %d\n", unmaskedPtr);
			int pointerType = unmaskedPtr[0];
//			printf("pointerType: %d\n", pointerType);
//			printf("addressOfThing: %d\n", (unmaskedPtr+1));
			switch(pointerType){
				case 1:
					{
						printf("Closure\n");
					}
				case 2:
					{	
						//printf("String: %s\n", ((char*)(unmaskedPtr[2])));
						printf("%s\n", ((char*)(unmaskedPtr[2])));
					}
				break;				
				case 3:
					{	
						//printf("Float: %f\n", *((float*)(unmaskedPtr+1)));
						printf("%f\n", *((float*)(unmaskedPtr+1)));
					}
				break;
				case 6:
					{
						printf("Plain Object\n");
					}
				break;
				case 7:
					{
						//printf("TempFloat: %f\n", *((float*)(unmaskedPtr+1)));
						printf("%f\n", *((float*)(unmaskedPtr+1)));
					}
				break;
				default:
					printf("I don't know how to print this!\n");
				break;
			}
		}
		break;
		case 3: // boolean
	//		printf("type boolean = %d\n", type);
//			printf("boolean: %u = ", val);
			printf("%s\n", (val == 7)?("True"):("False"));
		break;
		case 2: // null
		//	printf("type void = %d\n", type);
			printf("Null\n");
		break;
	}
	fflush(stdout);
	return;
}

void printmarker(unsigned int val){
	switch(val){
		case 1:
			printf("a\n");
		break;
		case 2:
			printf("b\n");
		break;
		case 3:
			printf("c\n");
		break;
	}
	return;
}
